package com.ehome.poct.base.log.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.ehome.core.service.AbstractCrudServiceImpl;
import com.ehome.exception.BizException;
import com.ehome.oss.config.OssClient;
import com.ehome.oss.properties.OssProperties;
import com.ehome.poct.base.api.constant.SyncStatus;
import com.ehome.poct.base.api.dto.device.TestDeviceDTO;
import com.ehome.poct.base.api.dto.log.*;
import com.ehome.poct.base.api.virtual.TestDeviceService;
import com.ehome.poct.base.api.virtual.log.TestDeviceSyncLogRecordService;
import com.ehome.poct.base.log.repository.TestDeviceSyncLogRecordRepository;
import com.ehome.poct.base.mqtt.MqttGateway;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 设备日志同步记录表SerivceImlp
 *
 * @author 路飞
 * @Date 2021-12-22 11:26
 */
@Service
@Slf4j
public class TestDeviceSyncLogRecordServiceImlp extends AbstractCrudServiceImpl<TestDeviceSyncLogRecordDTO> implements TestDeviceSyncLogRecordService {

    @Autowired
    private MqttGateway mqttGateway;
    @Autowired(required = false)
    private RedissonClient redissonClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    OssClient ossClient;
    @Autowired
    private OssProperties ossProperties;
    @Autowired
    TestDeviceService testDeviceService;
    @Autowired
    TestDeviceSyncLogRecordRepository testDeviceSyncLogRecordRepository;

    private static final String TOPIC = "ehome_device_mqtt_msg_";
    private static final String REDIS_KEY = "ehome_device_";

    @Override
    protected void preSave(TestDeviceSyncLogRecordDTO dto, Map<String, Object> map) {
        if(StrUtil.isEmpty(dto.getOssPath())){
            throw new BizException("ossPath 不能为空");
        }
        setTermClinic(dto);
    }

    @Override
    protected void postSave(TestDeviceSyncLogRecordDTO dto, Map<String, Object> map) {
        log.info("postSave sno:{}",dto.getSno());
        sendToMqtt(dto);
    }

    @Override
    public SyncLogCallbackDTO getSyncLogRecord(String sno) {
        TestDeviceDTO tdd = new TestDeviceDTO();
        tdd.setSno(sno);
        TestDeviceDTO dto = testDeviceService.selectOne(tdd);
        if(dto == null){
            throw new BizException("未查询到任何设备，请确认后再试");
        }
        //发送MQTT到硬件
        sendMqttToHardware(sno);
        //堵塞方法等待硬件回调
        //获得锁
        RCountDownLatch latch = redissonClient.getCountDownLatch(REDIS_KEY.concat(sno));
        //添加记数器
        latch.trySetCount(1);
        //等待
        try {
            boolean await = latch.await(10, TimeUnit.SECONDS);
            if(!await){
                throw new BizException("设备未开机，请保证设备处于开机状态再试");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new BizException("同步锁发生错误");
        }
        String s = stringRedisTemplate.opsForValue().get(sno);
        return JSON.parseObject(s,SyncLogCallbackDTO.class);
    }

    private void sendMqttToHardware(String sno) {
        SyncLogMqttDTO dto = new SyncLogMqttDTO();
        dto.setPubAuthor("伊鸿健康");
        dto.setPublishTime(DateUtil.now());
        dto.setTitle("获取日志清单列表");
        dto.setType(5);
        dto.setReceivePackageId("com.ehome.logcat.service");
        dto.setData(new SyncLogMqttDTO.MqttData(sno));
        mqttGateway.sendToMqtt(JSON.toJSONString(dto),TOPIC.concat(sno));
    }

    @Override
    public void syncLogCallback(SyncLogCallbackDTO dto) {
        stringRedisTemplate.opsForValue().set(dto.getSno(), JSON.toJSONString(dto));
        RCountDownLatch latch = redissonClient.getCountDownLatch(REDIS_KEY.concat(dto.getSno()));
        latch.countDown();
    }

    @Override
    public void logUploadCallback1(LogUploadCallbackDTO dto) {
        TestDeviceSyncLogRecordDTO tdd = this.getById(dto.getDataId());
        if(tdd == null){
            throw new BizException("同步记录不存在");
        }
        tdd.setSyncStatus(SyncStatus.PACKAGING.ordinal());
        testDeviceSyncLogRecordRepository.update(tdd.getSyncLogRecordId(), tdd);
    }

    @Override
    public void logUploadCallback2(LogUploadCallbackDTO dto) {
        TestDeviceSyncLogRecordDTO tdd = this.getById(dto.getDataId());
        String s = null;
        try {
            log.info("日志查询回调。sno：{}",tdd.getSno());
            s = packageUploadZip(tdd);
            tdd.setSyncStatus(SyncStatus.SYNC_COMPLETE.ordinal());
            tdd.setUrl(s);
            testDeviceSyncLogRecordRepository.update(tdd.getSyncLogRecordId(), tdd);
        } catch (Exception e) {
            log.info("日志查询回调失败。sno：{}",tdd.getSno());
            tdd.setSyncStatus(SyncStatus.SYNC_FAILED.ordinal());
            testDeviceSyncLogRecordRepository.update(tdd.getSyncLogRecordId(), tdd);
            throw e;
        }
    }

    @Override
    public TestDeviceSyncLogRecordDTO batchInsertSyncLog(List<TestDeviceSyncLogRecordDTO> list) {
        TestDeviceSyncLogRecordDTO dto = CollUtil.getFirst(list);
        setTermClinic(dto);
        List<String> ossPath = CollUtil.getFieldValues(list, "ossPath", String.class);
        dto.setOssPath(StrUtil.join(StrUtil.COMMA,ossPath));
        TestDeviceSyncLogRecordDTO insert = testDeviceSyncLogRecordRepository.insert(dto);
        log.info("batchInsertSyncLog sno:{}",insert.getSno());
        sendToMqtt(dto);
        return insert;
    }

    /**
     * 打包上传文件
     * @param dto
     */
    public String packageUploadZip(TestDeviceSyncLogRecordDTO dto) throws BizException{
        String url = dto.getSno().concat("/").concat(dto.getSyncLogRecordId().toString()).concat(".zip");
        try {
            //获取oss目录
            OSS ossClient = getOssClient();
            if(StrUtil.isEmpty(dto.getOssPath())){
                throw new BizException("同步地址不存在！");
            }
            String[] split = dto.getOssPath().split(StrUtil.COMMA);
            List<String> files = Lists.newArrayList();
            List<InputStream> inList = Lists.newArrayList();
            for (String path : split) {
                //列举文件
                ObjectListing objectListing = ossClient.listObjects(dto.getBucketName(), path);
                List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
                for (OSSObjectSummary s : sums) {
                    if (StrUtil.endWith(s.getKey(), ".zip")) {
                        continue;
                    }
                    OSSObject ossObject = ossClient.getObject(dto.getBucketName(), s.getKey());
                    InputStream objectContent = ossObject.getObjectContent();
                    files.add(s.getKey());
                    inList.add(objectContent);
                }
            }

            ByteArrayOutputStream zos = new ByteArrayOutputStream();
            ZipUtil.zip(zos, files.toArray(new String[files.size()]), inList.toArray(new InputStream[inList.size()]));
            ByteArrayInputStream input = IoUtil.toStream(zos);
            ossClient.putObject(dto.getBucketName(), url, input);


            dto.setLogSize(zos.size());
            dto.setSyncLogNum(files.size());

            input.close();
            ossClient.shutdown();

            return ossClient.generatePresignedUrl(dto.getBucketName(),url,DateUtil.offsetMonth(new DateTime(), 12)).toString();
        } catch (Exception e) {
           throw new BizException("同步失败 失败原因:"+e.getMessage());
        }
    }

    private void setTermClinic(TestDeviceSyncLogRecordDTO dto) {
        TestDeviceDTO tdd = new TestDeviceDTO();
        tdd.setSno(dto.getSno());
        TestDeviceDTO testDeviceDTO = testDeviceService.selectOne(tdd);
        if(testDeviceDTO != null){
            dto.setTermClinicId(testDeviceDTO.getTermClinicId());
        }
    }

    private void sendToMqtt(TestDeviceSyncLogRecordDTO dto) {
        //发送MQTT到硬件
        UploadSyncLogMqttDTO dto2 = new UploadSyncLogMqttDTO();
        dto2.setPubAuthor("伊鸿健康");
        dto2.setPublishTime(DateUtil.now());
        dto2.setTitle("通知上传选择的日志");
        dto2.setType(6);
        dto2.setReceivePackageId("com.ehome.logcat.service");
        UploadSyncLogMqttDTO.MqttData mqttData = new UploadSyncLogMqttDTO.MqttData();
        mqttData.setBucketName(dto.getBucketName());
        mqttData.setOssPathType(dto.getOssPathType());
        mqttData.setSno(dto.getSno());
        mqttData.setDataId(dto.getSyncLogRecordId());
        String[] split = dto.getOssPath().split(StrUtil.COMMA);
        mqttData.setOssPath(Arrays.asList(split));
        dto2.setData(mqttData);
        log.info("发生mqtt,参数：{}",dto2);
        mqttGateway.sendToMqtt(JSON.toJSONString(dto2),TOPIC.concat(dto.getSno()));
    }

    private OSS getOssClient() {
        // 创建OSSClient实例。
        return new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
    }
}